<?php
/*
 * @Description    : 邮件发送任务
 * @Version        : 1.0.0
 * @Author         : QianLong
 * @Date           : 2020-01-22 15:56:35
 * @LastEditors    : QianLong
 * @LastEditTime   : 2024-01-16 17:02:49
 */

declare(strict_types=1);

namespace App\Job;

use App\Service\Common;
use Hyperf\AsyncQueue\Job;
use Symfony\Component\Mailer\Transport;
use Symfony\Component\Mailer\Mailer;
use Symfony\Component\Mime\Email;
use Symfony\Component\Mime\Address;
use Symfony\Component\Mailer\Exception\TransportExceptionInterface;

class SymfonyEmailJob extends Job
{
    public $params;
    public $redisCache;

    public function __construct($params, $redisCache)
    {
        $this->params = $params;
        $this->redisCache = $redisCache;
    }

    public function handle()
    {
        if (!empty($this->params) && is_array($this->params)) {
            $serverInfo = $this->redisCache->getEmailServer($this->params['es_id']);
            $mailDsn = 'smtp://' . $serverInfo['smtp_username'] . ':' . $serverInfo['smtp_password'] . '@' . $serverInfo['smtp_server'] . ':' . $serverInfo['port'];
            if ((int)$serverInfo['is_ssl'] == 1) {
                $mailDsn = 'smtps://' . $serverInfo['smtp_username'] . ':' . $serverInfo['smtp_password'] . '@' . $serverInfo['smtp_server'] . ':' . $serverInfo['port'];
            }
            $transport = Transport::fromDsn($mailDsn);
            $mailer = new Mailer($transport);
            $emailObj = (new Email())
                ->from(new Address($serverInfo['sender'], $serverInfo['sender_name']));
            if (isset($this->params['reply_email'])) {
                $emailObj->replyTo($this->params['reply_email']);
            }
            $emailSetting = $this->redisCache->getEmailSetting($this->params['uid']);

            \Hyperf\Retry\Retry::whenReturns(false) // 当返回false时重试
                ->max(2) // 最多5次
                ->inSeconds(30 * 60) // 最长30*60秒
                ->sleep(15 * 1000) // 间隔30秒
                ->fallback(function () use ($emailSetting, $mailer, $emailObj) {
                    return $this->doSend($emailSetting, $mailer, $emailObj);
                }) // fallback函数
                ->call(function () use ($emailSetting, $mailer, $emailObj) {
                    return $this->doSend($emailSetting, $mailer, $emailObj);
                });

            unset($emailArray, $in, $emailSetting, $emailArray2);
        }
    }
    public function doSend($emailSetting, $mailer, $emailObj)
    {
        if (empty($this->params['single_email'])) {
            return true;
        }
        $title = (isset($this->params['title']) && !empty($this->params['title'])) ? $this->params['title'] : '邮件通知';
        if (isset($this->params['et_id']) && !empty($this->params['et_id'])) {
            $content = $this->redisCache->getEmailTemplateRealTime($this->params['et_id']);
            if (empty($content)) {
                $content = $this->params['content'];
            }
        } else {
            $content = $this->params['content'];
        }
        $content = htmlspecialchars_decode($content);
        $content = str_replace('#content#', $this->params['content'], $content);
        if (isset($this->params['single_email_user_name']) && !empty($this->params['single_email_user_name'])) {
            $content = str_replace('#user_name#', $this->params['single_email_user_name'], $content);
        }
        foreach ($this->params as $key => $vo) {
            if (strpos($key, 'ext-') !== false) {
                $keyArray = explode('-', $key);
                if (isset($keyArray[0]) && $keyArray[0] == 'ext') {
                    $content = str_replace('#ext-' . $keyArray[1] . '#', $vo, $content);
                }
            }
        }
        if (isset($this->params['esl_id']) && !empty($this->params['esl_id'])) {
            $postData['_id'] = $this->params['esl_id'];
        } else {
            $postData['_id'] = createULId('ESL');
            $postData['et_id'] = (isset($this->params['et_id']) && !empty($this->params['et_id']))?$this->params['et_id']:'';
            $postData['uid'] = $this->params['uid'];
            $postData['es_id'] = $this->params['es_id'];
            $postData['user_name'] = $this->params['single_email_user_name'];
            $postData['content'] = $this->params['content'];
            $postData['send_content'] = $content;
            $postData['email'] = $this->params['single_email'];
            $params = $this->params;
            unset($params['uid'], $params['single_email'], $params['single_email_user_name']);
            $postData['params'] = json_encode($params, JSON_UNESCAPED_UNICODE);
            unset($params);
            $postData['title'] = $title;
            $postData['send_result'] = false;
            $postData['send_error'] = '';
            $postData['send_result_data'] = '';
            $postData['is_read'] = 0;
            $postData['read_time'] = 0;
            $postData['send_time'] = time();
            $postData['date'] = date('Ymd');
            $postData['hour'] = date('YmdH');
            $postData['only_hour'] = date('H') . ':00';
            $postData['hour_stamp'] = strtotime(date('Y-m-d H') . ':00:00');
            $postData['month'] = date('Ym');
            $postData['year'] = date('Y');
            $collection = mongodb2()->selectCollection('send_email_log_' . $this->params['uid']);
            $collection->insertOne($postData);
            (new \App\Service\Common)->mongodbIndexCreate($collection, 'uid_send_time', ['uid' => 1, 'send_time' => -1]);
            (new \App\Service\Common)->mongodbIndexCreate($collection, 'uid_send_time_es_id', ['uid' => 1, 'send_time' => -1, 'es_id' => -1]);
        }
        if (!isset($this->params['ignore_limit']) || $this->params['ignore_limit'] != 1) {
            $day_limit = $this->redisCache->sendLimit($this->params['es_id'] . ':' . $this->params['single_email'], 'day');
            if (!empty($day_limit) && !empty($emailSetting['day_limit']) && $day_limit > $emailSetting['day_limit']) {
                $collection->updateOne(['_id' => $postData['_id']], ['$set' => ['send_error' => '超过单个自然日内发送上限，发送失败']]);
                return true;
            }
            $hour_limit = $this->redisCache->sendLimit($this->params['es_id'] . ':' . $this->params['single_email'], 'hour');
            if (!empty($hour_limit) && !empty($emailSetting['hour_limit']) && $hour_limit > $emailSetting['hour_limit']) {
                $this->redisCache->sendLimitDec($this->params['es_id'] . ':' . $this->params['single_email'], 'day');
                $collection->updateOne(['_id' => $postData['_id']], ['$set' => ['send_error' => '超过每小时内发送上限，发送失败']]);
                return true;
            }
            $minute_limit = $this->redisCache->sendLimit($this->params['es_id'] . ':' . $this->params['single_email'], 'minute');
            if (!empty($minute_limit) && !empty($emailSetting['minute_limit']) && $minute_limit > $emailSetting['minute_limit']) {
                $this->redisCache->sendLimitDec($this->params['es_id'] . ':' . $this->params['single_email'], 'hour');
                $this->redisCache->sendLimitDec($this->params['es_id'] . ':' . $this->params['single_email'], 'day');
                $collection->updateOne(['_id' => $postData['_id']], ['$set' => ['send_error' => '超过每分钟内发送上限，发送失败']]);
                return true;
            }
        }
        $send_result = false;
        $send_result_data = '';
        $send_error = '';
        $send_id = (new Common)->encryptDevKEY(json_encode(['id'=> $postData['_id'],'uid'=> $postData['uid']]));
        try {
            $emailObj->to($this->params['single_email'])->subject($title)
                ->html($content . '<img src="https://21ds.cn/email/img?send_id=' . $send_id.'">');
            // 添加附件 可选
            if (isset($this->params['attachment'])) {
                $attachmentList = explode(',', $this->params['attachment']);
                foreach ($attachmentList as $url) {
                    $emailObj->attachFromPath($url);
                    // $emailObj->attachFromPath(new DataPart(new File($url)));
                }
            }
            $send_result_data = $mailer->send($emailObj);
            $send_result = true;
        } catch (TransportExceptionInterface $exception) {
            $send_error = '(' . $exception->getCode() . ') ' . $exception->getMessage();
        }
        $collection->updateOne(['_id' => $postData['_id']], ['$set' => ['send_error' => $send_error, 'send_result' => $send_result, 'send_result_data' => $send_result_data]]);
        unset($postData);
        return true;
    }
}
